package org.garbagecan.quartzstudy.cron;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

import org.quartz.Calendar;
import org.quartz.CronExpression;
import org.quartz.impl.calendar.BaseCalendar;

public class MultiCronCalendar extends BaseCalendar {
	static final long serialVersionUID = -8172103999750856831L;

	List<CronExpression> cronExpressions;

	private TimeZone timeZone;

	public MultiCronCalendar(List<String> expressions) throws ParseException {
		this(null, expressions, null);
	}

	public MultiCronCalendar(Calendar baseCalendar, List<String> expressions)
			throws ParseException {
		this(baseCalendar, expressions, null);
	}

	public MultiCronCalendar(Calendar baseCalendar, List<String> expressions,
			TimeZone timeZone) throws ParseException {
		super(baseCalendar);
		this.cronExpressions = new ArrayList<CronExpression>();
		for (String expression : expressions) {
			cronExpressions.add(new CronExpression(expression));
		}
		this.timeZone = timeZone;
		for (CronExpression cronExpression : cronExpressions) {
			cronExpression.setTimeZone(timeZone);
		}
	}

	public TimeZone getTimeZone() {
		return this.timeZone;
	}

	public void setTimeZone(TimeZone timeZone) {
		this.timeZone = timeZone;
		for (CronExpression cronExpression : cronExpressions) {
			cronExpression.setTimeZone(timeZone);
		}
	}

	public boolean isTimeIncluded(long timeInMillis) {
		if ((getBaseCalendar() != null)
				&& (getBaseCalendar().isTimeIncluded(timeInMillis) == false)) {
			return false;
		}
		for (CronExpression cronExpression : this.cronExpressions) {
			if (cronExpression.isSatisfiedBy(new Date(timeInMillis))) {
				return false;
			}
		}
		return true;
	}

	public long getNextIncludedTime(long timeInMillis) {
		List<Long> list = new ArrayList<Long>();
		for (CronExpression cronExpression : this.cronExpressions) {
			long nextIncludedTime = timeInMillis + 1; // plus on millisecond
			while (!isTimeIncluded(nextIncludedTime)) {
				if (cronExpression.isSatisfiedBy(new Date(nextIncludedTime))) {
					nextIncludedTime = cronExpression.getNextInvalidTimeAfter(
							new Date(nextIncludedTime)).getTime();
				} else if ((getBaseCalendar() != null)
						&& (!getBaseCalendar().isTimeIncluded(nextIncludedTime))) {
					nextIncludedTime = getBaseCalendar().getNextIncludedTime(
							nextIncludedTime);
				} else {
					nextIncludedTime++;
				}
			}
			list.add(nextIncludedTime);
		}
		Collections.sort(list);
		return list.get(0);
	}
}